#include "wingelfparser.h"
#include "settingdialog.h"
#include "settings.h"
#include <QApplication>
#include <QMessageBox>
#include <QTreeWidgetItem>
#include <elf.h>

#define ICONRES(name) QIcon(":/WingElfParser/image/" name ".png")
#define HOSTICONRES(name) QIcon(HOSTRESPIMG(name))

#define QTWI(parent, title, offset, len)                                       \
  p = new QTreeWidgetItem(parent, {title});                                    \
  p->setData(0, Qt::UserRole, QVariant::fromValue(Analysis{offset, len}));

#define TOINT64(BYTES) (*reinterpret_cast<qint64 *>(BYTES.data()))
#define TOUINT(BYTES) (*reinterpret_cast<uint *>(BYTES.data()))
#define TOUSHORT(BYTES) (*reinterpret_cast<ushort *>(BYTES.data()))
#define TOINT(BYTES) (*reinterpret_cast<int *>(BYTES.data()))
#define TOCHARINT(BYTES) (int(BYTES[0]))

WingElfPlugin::WingElfPlugin(QObject *parent) { Q_UNUSED(parent); }

WingElfPlugin::~WingElfPlugin() {}

#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(WingElfParser, GenericPlugin)
#endif // QT_VERSION < 0x050000

int WingElfPlugin::sdkVersion() { return SDKVERSION; }

const QString WingElfPlugin::signature() { return WINGSUMMER; }

const QString WingElfPlugin::pluginName() { return tr("WingElfParser"); }

const QString WingElfPlugin::pluginAuthor() { return WINGSUMMER; }

const QString WingElfPlugin::pluginComment() {
  return tr("A Marker plugin for Elf");
}

bool WingElfPlugin::init(QList<WingPluginInfo> loadedplugin) {
  Q_UNUSED(loadedplugin);

  if (SDKVERSION < 8) {
    QMessageBox::critical(nullptr, "Error",
                          "UnSupported Plugin System Version!",
                          QMessageBox::Ok);
    return false;
  }

  auto s = GETPLUGINQM("WingElfParser.qm");
  if (!translator.load(s) || !QApplication::installTranslator(&translator)) {
    QMessageBox::critical(nullptr, "Error", "Error Loading Translation File!",
                          QMessageBox::Ok);
    return false;
  }

  auto icon = ICONRES("icon");

  PluginMenuInitBegin(menu, tr("WingElfParser")) {
    menu->setIcon(icon);
    PluginMenuAddItemCheckLamba(
        menu, tr("AddMetaData"), Settings::instance()->addMetadata(), [=] {
          auto settings = Settings::instance();
          settings->setAddMetadata(!settings->addMetadata());
        });
    PluginMenuAddItemIconAction(menu, tr("Analyse"), ICONRES("analyse"),
                                WingElfPlugin::analyse);
    menu->addSeparator();
    PluginMenuAddItemIconAction(menu, tr("Setting"), HOSTICONRES("setting"),
                                WingElfPlugin::on_setting);
    PluginMenuAddItemIconLamba(menu, tr("Author"), HOSTICONRES("author"), [=] {
      auto authord = newAboutDialog(
          QPixmap(), {":/WingElfParser", ":/WingElfParser/image"});
      authord->exec();
      delete authord;
    });
    PluginMenuAddItemIconLamba(menu, tr("Sponsor"), HOSTICONRES("sponsor"),
                               [=] {
                                 auto sponsor = newSponsorDialog();
                                 sponsor->exec();
                                 delete sponsor;
                               });
  }
  PluginMenuInitEnd();

  tw = new QTreeWidget;
  tw->setEditTriggers(QTreeWidget::NoEditTriggers);
  tw->setHeaderLabels({QString(10, ' '), tr("Value")});
  tw->setColumnCount(2);

  QMenu *tmenu;
  PluginMenuInitBegin(tmenu, "") {
    PluginMenuAddItemIconAction(tmenu, tr("Analyse"), ICONRES("analyse"),
                                WingElfPlugin::analyse);
    PluginMenuAddItemIconAction(tmenu, tr("Setting"), HOSTICONRES("setting"),
                                WingElfPlugin::on_setting);
  }
  PluginMenuInitEnd();
  PluginToolButtonInit(tbtn, tmenu, icon);
  connect(tw, &QTreeWidget::itemSelectionChanged, this, [=] {
    if (reader.currentDocFilename() != filename)
      return;
    auto s = tw->selectedItems();
    if (s.count()) {
      auto item = s.first();
      auto id = item->data(0, Qt::UserRole).value<Analysis>();
      USINGCONTROL({
        controller.enabledCursor(true);
        controller.select(id.offset, int(id.length));
      });
    }
  });

  PluginDockWidgetInit(dw, tw, tr("WingElfParser"), "wingelf");
  return true;
}

void WingElfPlugin::plugin2MessagePipe(WingPluginMessage type,
                                       QList<QVariant> msg) {
  Q_UNUSED(type);
  Q_UNUSED(msg);
}

void WingElfPlugin::on_setting() {
  SettingDialog s(this);
  s.show();
}

void WingElfPlugin::analyse() {
  if (reader.currentDoc() < 0) {
    toast(ICONRES("icon"), tr("NoFile"));
  } else {
    if (this->parse()) {
      toast(ICONRES("icon"), tr("Success"));
    } else {
      QMessageBox::critical(nullptr, tr("WingElfParser"), tr("Error"));
    }
  }
}

bool WingElfPlugin::parse() {
  auto totalbytes = reader.documentBytes();
  if (totalbytes <= sizeof(Elf32_Ehdr))
    return false;

  auto id = reader.read(0, EI_NIDENT);
  if (!id.startsWith(ELFMAG))
    return false;

  auto clsbit = int(reader.read(EI_CLASS, 1)[0]);

  tw->clear();
  filename = reader.currentDocFilename();
  tw->setHeaderLabel(filename);

  switch (clsbit) {
  case ELFCLASS32:
    return parse32();
    break;
  case ELFCLASS64:
    return parse64();
    break;
  }
  return false;
}

bool WingElfPlugin::parse32() {
  auto totalb = reader.documentBytes();

  auto settings = Settings::instance();
  auto addmeta = settings->addMetadata();
  if (requestControl()) {
    QTreeWidgetItem *p;

    if (addmeta)
      controller.setMetaVisible(false);

    QTWI(tw, "struct elf_header", 0, sizeof(Elf32_Ehdr));

    if (addmeta)
      controller.metadata(0, sizeof(Elf32_Ehdr),
                          settings->getColor(SettingColor::elf_header_color),
                          Qt::transparent, QString());
    auto h = p;
    QTWI(p, "struct e_ident_t e_ident", 0, EI_NIDENT);
    auto hh = p;
    QTWI(hh, "char file_identification[4]", 0, 4);
    p->setText(1, reader.read(0, 4));
    auto off = 4;

    auto str = reader.read(off, 1);
    QTWI(hh, "enum ei_class_2_e ei_class_2", off++, 1);
    auto m = QMetaEnum::fromType<ELFHelper::ei_class_2_e>();
    p->setText(1, m.valueToKey(str[0]));

    str = reader.read(off, 1);
    QTWI(hh, "enum ei_data_e ei_data", off++, 1);
    m = QMetaEnum::fromType<ELFHelper::ei_data_e>();
    p->setText(1, m.valueToKey(str[0]));

    str = reader.read(off, 1);
    QTWI(hh, "enum ei_version_e ei_version", off++, 1);
    m = QMetaEnum::fromType<ELFHelper::ei_version_e>();
    p->setText(1, m.valueToKey(str[0]));

    str = reader.read(off, 1);
    QTWI(hh, "enum ei_osabi_e ei_osabi", off++, 1);
    m = QMetaEnum::fromType<ELFHelper::ei_osabi_e>();
    p->setText(1, m.valueToKey(str[0]));

    str = reader.read(off, 1);
    QTWI(hh, "uchar ei_abiversion", off++, 1);
    p->setText(1, QString("%1").arg(TOCHARINT(str)));

    str = reader.read(off, 6).toHex(' ');
    QTWI(hh, "uchar ei_pad[6]", off, 6);
    p->setText(1, str);
    off += 6;

    str = reader.read(off, 1);
    QTWI(hh, "uchar ei_nident_SIZE", off++, 1);
    p->setText(1, QString("%1").arg(TOCHARINT(str)));

    str = reader.read(off, 2);
    QTWI(h, "enum e_type32_e e_type", off, 2);
    m = QMetaEnum::fromType<ELFHelper::e_type_e>();
    p->setText(1, m.valueToKey(TOUSHORT(str)));
    off += 2;

    str = reader.read(off, 2);
    QTWI(h, "enum e_machine32_e e_machine", off, 2);
    m = QMetaEnum::fromType<ELFHelper::e_machine_e>();
    p->setText(1, m.valueToKey(TOUSHORT(str)));
    off += 2;

    str = reader.read(off, 4);
    QTWI(h, "enum e_version64_e e_version", off, 4);
    m = QMetaEnum::fromType<ELFHelper::e_version_e>();
    p->setText(1, m.valueToKey(TOINT(str)));
    off += 4;

    QTWI(h, "Elf32_Addr e_entry_START_ADDRESS", off, 4);
    p->setText(1, QString("0x%1").arg(TOUINT(reader.read(off, 4)), 0, 16));
    off += 4;

    auto e_phoff = TOUINT(reader.read(off, 4));
    QTWI(h, "Elf32_Off e_phoff_PROGRAM_HEADER_OFFSET_IN_FILE", off, 4);
    p->setText(1, QString("%1").arg(e_phoff));
    off += 4;

    auto e_shoff = TOUINT(reader.read(off, 4));
    QTWI(h, "Elf32_Off e_shoff_SECTION_HEADER_OFFSET_IN_FILE", off, 4);
    p->setText(1, QString("%1").arg(e_shoff));
    off += 4;

    QTWI(h, "Elf32_Word e_flags", off, 4);
    p->setText(1, QString("0x%1").arg(TOUINT(reader.read(off, 4)), 0, 16));
    off += 4;

    QTWI(h, "Elf32_Half e_ehsize_ELF_HEADER_SIZE", off, 2);
    p->setText(1, QString("%1").arg(TOUINT(reader.read(off, 4))));
    off += 2;

    auto e_phentsize = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf32_Half e_phentsize_PROGRAM_HEADER_ENTRY_SIZE_IN_FILE", off, 2);
    p->setText(1, QString("%1").arg(TOUSHORT(reader.read(off, 2))));
    off += 2;

    auto e_phnum = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf32_Half e_phnum_NUMBER_OF_PROGRAM_HEADER_ENTRIES", off, 2);
    p->setText(1, QString("%1").arg(TOUSHORT(reader.read(off, 2))));
    off += 2;

    auto e_shentsize = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf32_Half e_shentsize_SECTION_HEADER_ENTRY_SIZE", off, 2);
    p->setText(1, QString("%1").arg(TOUSHORT(reader.read(off, 2))));
    off += 2;

    auto e_shnum = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf32_Half e_shnum_NUMBER_OF_SECTION_HEADER_ENTRIES", off, 2);
    p->setText(1, QString("%1").arg(TOUSHORT(reader.read(off, 2))));
    off += 2;

    auto e_shtrndx = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf64_Half e_shtrndx_STRING_TABLE_INDEX", off, 2);
    p->setText(1, QString("%1").arg(e_shtrndx));
    off += 2;

    if (e_phoff > totalb || e_phoff + e_phnum * e_phentsize > totalb)
      goto err;

    if (e_phoff && e_phnum) {
      QTWI(tw, "struct program_header_table", e_phoff, e_phentsize * e_phnum);
      auto pr = p;
      auto o = e_phoff;
      auto color = settings->getColor(SettingColor::program_table_color);
      auto dcolor = color.darker();
      auto b = false;

      for (int i = 0; i < e_phnum; i++, o += e_phentsize) {
        QTWI(pr,
             QString("struct program_table_entry32_t program_table_element[%1]")
                 .arg(i),
             o, e_phentsize);
        if (addmeta)
          controller.metadata(o, o + e_phentsize, b ? color : dcolor,
                              Qt::transparent, QString());
        auto ph = p;
        auto oo = o;
        QTWI(ph, "enum p_type32_e p_type", oo, 4);
        m = QMetaEnum::fromType<ELFHelper::p_type_e>();
        auto type = m.valueToKey(TOINT(reader.read(oo, 4)));
        auto t = type ? type : "Unknown";

        b = !b;
        p->setText(1, t);
        oo += 4;
        QTWI(ph, "Elf32_Off p_offset_FROM_FILE_BEGIN", oo, 4);
        p->setText(1, QString("0x%1").arg(TOUINT(reader.read(off, 4)), 0, 16));
        oo += 4;
        QTWI(ph, "Elf32_Addr p_vaddr_VIRTUAL_ADDRESS", oo, 4);
        p->setText(1, QString("0x%1").arg(TOUINT(reader.read(off, 4)), 0, 16));
        oo += 4;
        QTWI(ph, "Elf32_Addr p_paddr_PHYSICAL_ADDRESS", oo, 4);
        p->setText(1, QString("0x%1").arg(TOUINT(reader.read(off, 4)), 0, 16));
        oo += 4;
        QTWI(ph, "Elf32_Word p_filesz_SEGMENT_FILE_LENGTH", oo, 4);
        p->setText(1, QString("%1").arg(TOINT(reader.read(off, 4))));
        oo += 4;
        QTWI(ph, "Elf32_Word p_memsz_SEGMENT_RAM_LENGTH", oo, 4);
        p->setText(1, QString("%1").arg(TOINT(reader.read(off, 4))));
        oo += 4;
        QTWI(ph, "enum p_flags32_e p_flags", oo, 4);
        m = QMetaEnum::fromType<ELFHelper::p_flags_e>();
        auto rwx = TOINT(reader.read(oo, 4));
        p->setText(1, m.valueToKey(rwx));
        QString _rwx;
        switch (rwx) {
        case ELFHelper::p_flags_e::PF_None:
          _rwx = "___";
          break;
        case ELFHelper::p_flags_e::PF_Exec:
          _rwx = "__X";
          break;
        case ELFHelper::p_flags_e::PF_Write:
          _rwx = "_W_";
          break;
        case ELFHelper::p_flags_e::PF_Write_Exec:
          _rwx = "_WX";
          break;
        case ELFHelper::p_flags_e::PF_Read:
          _rwx = "R__";
          break;
        case ELFHelper::p_flags_e::PF_Read_Exec:
          _rwx = "R_X";
          break;
        case ELFHelper::p_flags_e::PF_Read_Write:
          _rwx = "RW_";
          break;
        case ELFHelper::p_flags_e::PF_Read_Write_Exec:
          _rwx = "RWX";
          break;
        }

        oo += 4;
        QTWI(ph, "Elf32_Word p_align", oo, 4);
        p->setText(1, QString("%1").arg(TOINT(reader.read(oo, 4))));
        oo += 4;
        ph->setText(1, QString("(%1) %2").arg(_rwx).arg(t));
      }
    }

    if (e_shoff > totalb || e_shoff + e_shnum * e_shentsize > totalb)
      goto err;

    qint64 symoff = 0, dysymoff = 0;
    int symlink = 0, dysymlink = 0;
    int symsize = 0, dysymsize = 0;
    QList<qint64> soffs;

    if (e_shoff && e_shnum) {
      QTWI(tw, "struct section_header_table", e_shoff, e_shentsize * e_shnum);
      auto sr = p;
      auto o = e_shoff;
      auto nameoff =
          TOUINT(reader.read(e_shoff + (e_shentsize * e_shtrndx) + 16, 4));

      auto color = settings->getColor(SettingColor::section_header_color);
      auto dcolor = color.darker();
      auto b = false;

      for (int i = 0; i < e_shnum; i++, o += e_shentsize) {
        QTWI(sr,
             QString("struct section_table_entry32_t section_table_element[%1]")
                 .arg(i),
             o, e_shentsize);
        auto sh = p;
        auto oo = o;

        if (addmeta)
          controller.metadata(o, o + e_shentsize, b ? color : dcolor,
                              Qt::transparent, QString());

        QTWI(sh, "s_name32_t s_name", oo, 4);
        auto off = TOUINT(reader.read(oo, 4));
        QString l;
        if (off) {
          auto of = nameoff + off;
          auto pos = reader.searchForward(of, QByteArray(1, '\0'));
          l = reader.read(of, int(pos - of));
        } else {
          l = "<Undefined>";
        }
        p->setText(1, l);
        sh->setText(1, l);

        oo += 4;
        str = reader.read(oo, 4);
        QTWI(sh, "enum s_type32_e s_type", oo, 4);
        m = QMetaEnum::fromType<ELFHelper::s_type_e>();
        p->setText(1, m.valueToKey(TOINT(str)));
        oo += 4;
        str = reader.read(oo, 4);
        QTWI(sh, "enum s_flags32_e s_flags", oo, 4);
        m = QMetaEnum::fromType<ELFHelper::s_flags32_e>();
        p->setText(1, m.valueToKey(TOINT(str)));
        oo += 4;
        QTWI(sh, "Elf32_Addr s_addr", oo, 4);
        p->setText(1, QString("0x%1").arg(TOUINT(reader.read(oo, 4)), 0, 16));
        oo += 4;
        QTWI(sh, "Elf32_Off s_offset", oo, 4);
        off = TOUINT(reader.read(oo, 4));
        soffs.push_back(off);
        p->setText(1, QString("0x%1").arg(off, 0, 16));
        oo += 4;
        QTWI(sh, "Elf32_Xword s_size", oo, 4);
        auto off0 = TOINT(reader.read(oo, 4));
        p->setText(1, QString("%1").arg(off0));
        oo += 4;
        QTWI(sh, "Elf32_Word s_link", oo, 4);
        auto off1 = TOINT(reader.read(oo, 4));
        p->setText(1, QString("0x%1").arg(off1, 0, 16));
        if (!l.compare(".symtab")) {
          symoff = off;
          symsize = off0;
          symlink = off1;
        } else if (!l.compare(".dynsym")) {
          dysymoff = off;
          dysymsize = off0;
          dysymlink = off1;
        }
        oo += 4;
        QTWI(sh, "Elf32_Word s_info", oo, 4);
        p->setText(1, QString("0x%1").arg(TOUINT(reader.read(oo, 4)), 0, 16));
        oo += 4;
        QTWI(sh, "Elf32_Xword s_addralign", oo, 4);
        p->setText(1, QString("%1").arg(TOINT(reader.read(oo, 4))));
        oo += 4;
        QTWI(sh, "Elf32_Xword s_entsize", oo, 4);
        p->setText(1, QString("%1").arg(TOINT(reader.read(oo, 4))));
        oo += 4;
      }
    }

    if (symoff && symsize) {
      QTWI(tw, "struct symbol_table", symoff, symsize);
      auto nameoff = soffs[symlink];
      auto length = ulong(symsize) / sizeof(Elf32_Sym);
      auto sr = p;
      auto o = symoff;

      auto color = settings->getColor(SettingColor::symbol_color);
      auto dcolor = color.darker();
      auto b = false;

      for (ulong i = 0; i < length; i++, o += sizeof(Elf32_Sym)) {
        QTWI(sr, QString("struct Elf32_Sym symtab[%1]").arg(i), o,
             sizeof(Elf32_Sym));
        auto sh = p;
        auto oo = o;

        if (addmeta)
          controller.metadata(o, o + qint64(sizeof(Elf32_Sym)),
                              b ? color : dcolor, Qt::transparent, QString());

        QTWI(sh, "sym_name32_t sym_name", oo, 4);
        auto off = TOUINT(reader.read(oo, 4));
        QString l;
        if (off) {
          auto of = nameoff + off;
          auto pos = reader.searchForward(of, QByteArray(1, '\0'));
          l = reader.read(of, int(pos - of));
        } else {
          l = "<Undefined>";
        }
        p->setText(1, l);
        sh->setText(1, l);
        oo += 4;
        QTWI(sh, "Elf32_Addr sym_value", oo, 4);
        p->setText(1, QString("0x%1").arg(TOUINT(reader.read(oo, 4)), 0, 16));
        oo += 4;
        QTWI(sh, "Elf32_Xword sym_size", oo, 4);
        p->setText(1, QString("%1").arg(TOINT(reader.read(oo, 4))));
        oo += 4;
        str = reader.read(oo, 1);
        auto in = TOCHARINT(str);
        QTWI(sh, "sym_info_t sym_info", oo, 1);
        auto shh = p;
        QTWI(shh, "uchar sym_info_type : 4", oo, 1);
        m = QMetaEnum::fromType<ELFHelper::sym_info_type_e>();
        auto t0 = m.valueToKey(in & 0xF);
        p->setText(1, t0);
        QTWI(shh, "uchar sym_info_bind : 4", oo++, 1);
        m = QMetaEnum::fromType<ELFHelper::sym_info_bind_e>();
        auto t1 = m.valueToKey((in & 0xF0) >> 4);
        p->setText(1, t1);
        shh->setText(1, QString("%1 | %2").arg(t0).arg(t1));

        str = reader.read(oo, 1);
        QTWI(sh, "unsigned char sym_other", oo++, 1);
        p->setText(1, QString("%1").arg(TOCHARINT(str)));
        QTWI(sh, "Elf32_Half sym_shndx", oo, 2);
        p->setText(1, QString("%1").arg(TOUSHORT(reader.read(oo, 2))));
        oo += 2;
      }
    }

    if (dysymoff && dysymsize) {
      QTWI(tw, "struct dynamic_symbol_table", dysymoff, dysymsize);
      auto nameoff = soffs[dysymlink];
      auto length = ulong(dysymsize) / sizeof(Elf32_Sym);
      auto sr = p;
      auto o = dysymoff;

      auto color = settings->getColor(SettingColor::dysymbol_color);
      auto dcolor = color.darker();
      auto b = false;

      for (ulong i = 0; i < length; i++, o += sizeof(Elf32_Sym)) {
        QTWI(sr, QString("struct Elf32_Sym symtab[%1]").arg(i), o,
             sizeof(Elf32_Sym));
        auto sh = p;
        auto oo = o;

        if (addmeta)
          controller.metadata(o, o + qint64(sizeof(Elf32_Sym)),
                              b ? color : dcolor, Qt::transparent, QString());

        QTWI(sh, "sym_name32_t sym_name", oo, 4);
        auto off = TOUINT(reader.read(oo, 4));
        QString l;
        if (off) {
          auto of = nameoff + off;
          auto pos = reader.searchForward(of, QByteArray(1, '\0'));
          l = reader.read(of, int(pos - of));
        } else {
          l = "<Undefined>";
        }
        p->setText(1, l);
        sh->setText(1, l);
        oo += 4;
        QTWI(sh, "Elf32_Addr sym_value", oo, 4);
        p->setText(1, QString("0x%1").arg(TOUINT(reader.read(oo, 4)), 0, 16));
        oo += 4;
        QTWI(sh, "Elf32_Xword sym_size", oo, 4);
        p->setText(1, QString("%1").arg(TOINT(reader.read(oo, 4))));
        oo += 4;
        str = reader.read(oo, 1);
        auto in = TOCHARINT(str);
        QTWI(sh, "sym_info_t sym_info", oo, 1);
        auto shh = p;
        QTWI(shh, "uchar sym_info_type : 4", oo, 1);
        m = QMetaEnum::fromType<ELFHelper::sym_info_type_e>();
        auto t0 = m.valueToKey(in & 0xF);
        p->setText(1, t0);
        QTWI(shh, "uchar sym_info_bind : 4", oo++, 1);
        m = QMetaEnum::fromType<ELFHelper::sym_info_bind_e>();
        auto t1 = m.valueToKey((in & 0xF0) >> 4);
        p->setText(1, t1);
        shh->setText(1, QString("%1 | %2").arg(t0).arg(t1));

        str = reader.read(oo, 1);
        QTWI(sh, "unsigned char sym_other", oo++, 1);
        p->setText(1, QString("%1").arg(TOCHARINT(str)));
        QTWI(sh, "Elf32_Half sym_shndx", oo, 2);
        p->setText(1, QString("%1").arg(TOUSHORT(reader.read(oo, 2))));
        oo += 2;
      }
    }
    goto inf;
  }
err:
  return false;
inf:
  if (addmeta)
    controller.setMetaVisible(true);
  requestRelease();
  return true;
}

bool WingElfPlugin::parse64() {
  auto totalb = reader.documentBytes();

  auto settings = Settings::instance();
  auto addmeta = settings->addMetadata();
  if (requestControl()) {
    QTreeWidgetItem *p;

    if (addmeta)
      controller.setMetaVisible(false);

    QTWI(tw, "struct elf_header", 0, sizeof(Elf64_Ehdr));

    if (addmeta)
      controller.metadata(0, sizeof(Elf64_Ehdr),
                          settings->getColor(SettingColor::elf_header_color),
                          Qt::transparent, QString());
    auto h = p;
    QTWI(p, "struct e_ident_t e_ident", 0, EI_NIDENT);
    auto hh = p;
    QTWI(hh, "char file_identification[4]", 0, 4);
    p->setText(1, reader.read(0, 4));
    auto off = 4;

    auto str = reader.read(off, 1);
    QTWI(hh, "enum ei_class_2_e ei_class_2", off++, 1);
    auto m = QMetaEnum::fromType<ELFHelper::ei_class_2_e>();
    p->setText(1, m.valueToKey(str[0]));

    str = reader.read(off, 1);
    QTWI(hh, "enum ei_data_e ei_data", off++, 1);
    m = QMetaEnum::fromType<ELFHelper::ei_data_e>();
    p->setText(1, m.valueToKey(str[0]));

    str = reader.read(off, 1);
    QTWI(hh, "enum ei_version_e ei_version", off++, 1);
    m = QMetaEnum::fromType<ELFHelper::ei_version_e>();
    p->setText(1, m.valueToKey(str[0]));

    str = reader.read(off, 1);
    QTWI(hh, "enum ei_osabi_e ei_osabi", off++, 1);
    m = QMetaEnum::fromType<ELFHelper::ei_osabi_e>();
    p->setText(1, m.valueToKey(str[0]));

    str = reader.read(off, 1);
    QTWI(hh, "uchar ei_abiversion", off++, 1);
    p->setText(1, QString("%1").arg(TOCHARINT(str)));

    str = reader.read(off, 6).toHex(' ');
    QTWI(hh, "uchar ei_pad[6]", off, 6);
    p->setText(1, str);
    off += 6;

    str = reader.read(off, 1);
    QTWI(hh, "uchar ei_nident_SIZE", off++, 1);
    p->setText(1, QString("%1").arg(TOCHARINT(str)));

    str = reader.read(off, 2);
    QTWI(h, "enum e_type64_e e_type", off, 2);
    m = QMetaEnum::fromType<ELFHelper::e_type_e>();
    p->setText(1, m.valueToKey(TOUSHORT(str)));
    off += 2;

    str = reader.read(off, 2);
    QTWI(h, "enum e_machine64_e e_machine", off, 2);
    m = QMetaEnum::fromType<ELFHelper::e_machine_e>();
    p->setText(1, m.valueToKey(TOUSHORT(str)));
    off += 2;

    str = reader.read(off, 4);
    QTWI(h, "enum e_version64_e e_version", off, 4);
    m = QMetaEnum::fromType<ELFHelper::e_version_e>();
    p->setText(1, m.valueToKey(TOINT(str)));
    off += 4;

    QTWI(h, "Elf64_Addr e_entry_START_ADDRESS", off, 8);
    p->setText(1, QString("0x%1").arg(TOINT64(reader.read(off, 8)), 0, 16));
    off += 8;

    auto e_phoff = TOINT64(reader.read(off, 8));
    QTWI(h, "Elf64_Off e_phoff_PROGRAM_HEADER_OFFSET_IN_FILE", off, 8);
    p->setText(1, QString("%1").arg(e_phoff));
    off += 8;

    auto e_shoff = TOINT64(reader.read(off, 8));
    QTWI(h, "Elf64_Off e_shoff_SECTION_HEADER_OFFSET_IN_FILE", off, 8);
    p->setText(1, QString("%1").arg(e_shoff));
    off += 8;

    QTWI(h, "Elf64_Word e_flags", off, 4);
    p->setText(1, QString("0x%1").arg(TOUINT(reader.read(off, 4)), 0, 16));
    off += 4;

    QTWI(h, "Elf64_Half e_ehsize_ELF_HEADER_SIZE", off, 2);
    p->setText(1, QString("%1").arg(TOUINT(reader.read(off, 4))));
    off += 2;

    auto e_phentsize = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf32_Half e_phentsize_PROGRAM_HEADER_ENTRY_SIZE_IN_FILE", off, 2);
    p->setText(1, QString("%1").arg(TOUSHORT(reader.read(off, 2))));
    off += 2;

    auto e_phnum = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf32_Half e_phnum_NUMBER_OF_PROGRAM_HEADER_ENTRIES", off, 2);
    p->setText(1, QString("%1").arg(TOUSHORT(reader.read(off, 2))));
    off += 2;

    auto e_shentsize = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf32_Half e_shentsize_SECTION_HEADER_ENTRY_SIZE", off, 2);
    p->setText(1, QString("%1").arg(TOUSHORT(reader.read(off, 2))));
    off += 2;

    auto e_shnum = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf32_Half e_shnum_NUMBER_OF_SECTION_HEADER_ENTRIES", off, 2);
    p->setText(1, QString("%1").arg(TOUSHORT(reader.read(off, 2))));
    off += 2;

    auto e_shtrndx = TOUSHORT(reader.read(off, 2));
    QTWI(h, "Elf64_Half e_shtrndx_STRING_TABLE_INDEX", off, 2);
    p->setText(1, QString("%1").arg(e_shtrndx));
    off += 2;

    if (quint64(e_phoff) > totalb ||
        quint64(e_phoff + e_phnum * e_phentsize) > totalb)
      goto err;

    if (e_phoff && e_phnum) {
      QTWI(tw, "struct program_header_table", e_phoff, e_phentsize * e_phnum);
      auto pr = p;
      auto o = e_phoff;
      auto color = settings->getColor(SettingColor::program_table_color);
      auto dcolor = color.darker();
      auto b = false;

      for (int i = 0; i < e_phnum; i++, o += e_phentsize) {
        QTWI(pr,
             QString("struct program_table_entry64_t program_table_element[%1]")
                 .arg(i),
             o, e_phentsize);
        if (addmeta)
          controller.metadata(o, o + e_phentsize, b ? color : dcolor,
                              Qt::transparent, QString());
        auto ph = p;
        auto oo = o;
        QTWI(ph, "enum p_type64_e p_type", oo, 4);
        m = QMetaEnum::fromType<ELFHelper::p_type_e>();
        auto type = m.valueToKey(TOINT(reader.read(oo, 4)));
        auto t = type ? type : "Unknown";

        b = !b;
        p->setText(1, t);
        oo += 4;
        QTWI(ph, "enum p_flags64_e p_flags", oo, 4);
        m = QMetaEnum::fromType<ELFHelper::p_flags_e>();
        auto rwx = TOINT(reader.read(oo, 4));
        p->setText(1, m.valueToKey(rwx));
        QString _rwx;
        switch (rwx) {
        case ELFHelper::p_flags_e::PF_None:
          _rwx = "___";
          break;
        case ELFHelper::p_flags_e::PF_Exec:
          _rwx = "__X";
          break;
        case ELFHelper::p_flags_e::PF_Write:
          _rwx = "_W_";
          break;
        case ELFHelper::p_flags_e::PF_Write_Exec:
          _rwx = "_WX";
          break;
        case ELFHelper::p_flags_e::PF_Read:
          _rwx = "R__";
          break;
        case ELFHelper::p_flags_e::PF_Read_Exec:
          _rwx = "R_X";
          break;
        case ELFHelper::p_flags_e::PF_Read_Write:
          _rwx = "RW_";
          break;
        case ELFHelper::p_flags_e::PF_Read_Write_Exec:
          _rwx = "RWX";
          break;
        }

        oo += 4;
        QTWI(ph, "Elf64_Off p_offset_FROM_FILE_BEGIN", oo, 8);
        p->setText(1, QString("0x%1").arg(TOINT64(reader.read(off, 8)), 0, 16));
        oo += 8;
        QTWI(ph, "Elf64_Addr p_vaddr_VIRTUAL_ADDRESS", oo, 8);
        p->setText(1, QString("0x%1").arg(TOINT64(reader.read(off, 8)), 0, 16));
        oo += 8;
        QTWI(ph, "Elf64_Addr p_paddr_PHYSICAL_ADDRESS", oo, 8);
        p->setText(1, QString("0x%1").arg(TOINT64(reader.read(off, 8)), 0, 16));
        oo += 8;
        QTWI(ph, "Elf64_XWord p_filesz_SEGMENT_FILE_LENGTH", oo, 8);
        p->setText(1, QString("%1").arg(TOINT64(reader.read(off, 8))));
        oo += 8;
        QTWI(ph, "Elf64_XWord p_memsz_SEGMENT_RAM_LENGTH", oo, 8);
        p->setText(1, QString("%1").arg(TOINT64(reader.read(off, 8))));
        oo += 8;
        QTWI(ph, "Elf64_XXWord p_align", oo, 8);
        p->setText(1, QString("%1").arg(TOINT64(reader.read(oo, 8))));
        oo += 8;
        ph->setText(1, QString("(%1) %2").arg(_rwx).arg(t));
      }
    }

    if (quint64(e_shoff) > totalb ||
        quint64(e_shoff + e_shnum * e_shentsize) > totalb)
      goto err;

    qint64 symoff = 0, dysymoff = 0;
    int symlink = 0, dysymlink = 0;
    qint64 symsize = 0, dysymsize = 0;
    QList<qint64> soffs;

    if (e_shoff && e_shnum) {
      QTWI(tw, "struct section_header_table", e_shoff, e_shentsize * e_shnum);
      auto sr = p;
      auto o = e_shoff;
      auto nameoff =
          TOINT64(reader.read(e_shoff + (e_shentsize * e_shtrndx) + 24, 8));

      auto color = settings->getColor(SettingColor::section_header_color);
      auto dcolor = color.darker();
      auto b = false;

      for (int i = 0; i < e_shnum; i++, o += e_shentsize) {
        QTWI(sr,
             QString("struct section_table_entry64_t section_table_element[%1]")
                 .arg(i),
             o, e_shentsize);
        auto sh = p;
        auto oo = o;

        if (addmeta)
          controller.metadata(o, o + e_shentsize, b ? color : dcolor,
                              Qt::transparent, QString());

        QTWI(sh, "s_name64_t s_name", oo, 4);
        auto off = TOUINT(reader.read(oo, 4));
        QString l;
        if (off) {
          auto of = nameoff + off;
          auto pos = reader.searchForward(of, QByteArray(1, '\0'));
          l = reader.read(of, int(pos - of));
        } else {
          l = "<Undefined>";
        }
        p->setText(1, l);
        sh->setText(1, l);

        oo += 4;
        str = reader.read(oo, 4);
        QTWI(sh, "enum s_type64_e s_type", oo, 4);
        m = QMetaEnum::fromType<ELFHelper::s_type_e>();
        p->setText(1, m.valueToKey(TOINT(str)));
        oo += 4;
        str = reader.read(oo, 4);
        QTWI(sh, "enum s_flags64_e s_flags", oo, 8);
        m = QMetaEnum::fromType<ELFHelper::s_flags64_e>();
        p->setText(1, m.valueToKey(TOINT(str)));
        oo += 8;
        QTWI(sh, "Elf64_Addr s_addr", oo, 8);
        p->setText(1, QString("0x%1").arg(TOINT64(reader.read(oo, 8)), 0, 16));
        oo += 8;
        QTWI(sh, "Elf64_Off s_offset", oo, 8);
        auto off00 = TOINT64(reader.read(oo, 8));
        soffs.push_back(off00);
        p->setText(1, QString("0x%1").arg(off00, 0, 16));
        oo += 8;
        QTWI(sh, "Elf64_Xword s_size", oo, 8);
        auto off0 = TOINT64(reader.read(oo, 8));
        p->setText(1, QString("%1").arg(off0));
        oo += 8;
        QTWI(sh, "Elf64_Word s_link", oo, 4);
        auto off1 = TOINT(reader.read(oo, 4));
        p->setText(1, QString("0x%1").arg(off1, 0, 16));
        if (!l.compare(".symtab")) {
          symoff = off00;
          symsize = off0;
          symlink = off1;
        } else if (!l.compare(".dynsym")) {
          dysymoff = off00;
          dysymsize = off0;
          dysymlink = off1;
        }
        oo += 4;
        QTWI(sh, "Elf64_Word s_info", oo, 4);
        p->setText(1, QString("0x%1").arg(TOUINT(reader.read(oo, 4)), 0, 16));
        oo += 4;
        QTWI(sh, "Elf64_Xword s_addralign", oo, 8);
        p->setText(1, QString("%1").arg(TOINT64(reader.read(oo, 8))));
        oo += 8;
        QTWI(sh, "Elf64_Xword s_entsize", oo, 8);
        p->setText(1, QString("%1").arg(TOINT64(reader.read(oo, 8))));
        oo += 8;
      }
    }

    if (symoff && symsize) {
      QTWI(tw, "struct symbol_table", symoff, symsize);
      auto nameoff = soffs[symlink];
      auto length = ulong(symsize) / sizeof(Elf64_Sym);
      auto sr = p;
      auto o = symoff;

      auto color = settings->getColor(SettingColor::symbol_color);
      auto dcolor = color.darker();
      auto b = false;

      for (ulong i = 0; i < length; i++, o += sizeof(Elf64_Sym)) {
        QTWI(sr, QString("struct Elf64_Sym symtab[%1]").arg(i), o,
             sizeof(Elf64_Sym));
        auto sh = p;
        auto oo = o;

        if (addmeta)
          controller.metadata(o, o + qint64(sizeof(Elf64_Sym)),
                              b ? color : dcolor, Qt::transparent, QString());

        QTWI(sh, "sym_name64_t sym_name", oo, 4);
        auto off = TOUINT(reader.read(oo, 4));
        QString l;
        if (off) {
          auto of = nameoff + off;
          auto pos = reader.searchForward(of, QByteArray(1, '\0'));
          l = reader.read(of, int(pos - of));
        } else {
          l = "<Undefined>";
        }
        p->setText(1, l);
        sh->setText(1, l);
        oo += 4;

        str = reader.read(oo, 1);
        auto in = TOCHARINT(str);
        QTWI(sh, "sym_info_t sym_info", oo, 1);
        auto shh = p;
        QTWI(shh, "uchar sym_info_type : 4", oo, 1);
        m = QMetaEnum::fromType<ELFHelper::sym_info_type_e>();
        auto t0 = m.valueToKey(in & 0xF);
        p->setText(1, t0);
        QTWI(shh, "uchar sym_info_bind : 4", oo++, 1);
        m = QMetaEnum::fromType<ELFHelper::sym_info_bind_e>();
        auto t1 = m.valueToKey((in & 0xF0) >> 4);
        p->setText(1, t1);
        shh->setText(1, QString("%1 | %2").arg(t0).arg(t1));
        str = reader.read(oo, 1);
        QTWI(sh, "unsigned char sym_other", oo++, 1);
        p->setText(1, QString("%1").arg(TOCHARINT(str)));
        QTWI(sh, "Elf64_Half sym_shndx", oo, 2);
        p->setText(1, QString("%1").arg(TOUSHORT(reader.read(oo, 2))));
        oo += 2;
        QTWI(sh, "Elf64_Addr sym_value", oo, 8);
        p->setText(1, QString("0x%1").arg(TOINT64(reader.read(oo, 8)), 0, 16));
        oo += 8;
        QTWI(sh, "Elf64_Xword sym_size", oo, 8);
        p->setText(1, QString("%1").arg(TOINT(reader.read(oo, 8))));
        oo += 8;
      }
    }

    if (dysymoff && dysymsize) {
      QTWI(tw, "struct dynamic_symbol_table", dysymoff, dysymsize);
      auto nameoff = soffs[dysymlink];
      auto length = ulong(dysymsize) / sizeof(Elf64_Sym);
      auto sr = p;
      auto o = dysymoff;

      auto color = settings->getColor(SettingColor::dysymbol_color);
      auto dcolor = color.darker();
      auto b = false;

      for (ulong i = 0; i < length; i++, o += sizeof(Elf64_Sym)) {
        QTWI(sr, QString("struct Elf64_Sym symtab[%1]").arg(i), o,
             sizeof(Elf32_Sym));
        auto sh = p;
        auto oo = o;

        if (addmeta)
          controller.metadata(o, o + qint64(sizeof(Elf64_Sym)),
                              b ? color : dcolor, Qt::transparent, QString());

        QTWI(sh, "sym_name64_t sym_name", oo, 4);
        auto off = TOUINT(reader.read(oo, 4));
        QString l;
        if (off) {
          auto of = nameoff + off;
          auto pos = reader.searchForward(of, QByteArray(1, '\0'));
          l = reader.read(of, int(pos - of));
        } else {
          l = "<Undefined>";
        }
        p->setText(1, l);
        sh->setText(1, l);
        oo += 4;

        str = reader.read(oo, 1);
        auto in = TOCHARINT(str);
        QTWI(sh, "sym_info_t sym_info", oo, 1);
        auto shh = p;
        QTWI(shh, "uchar sym_info_type : 4", oo, 1);
        m = QMetaEnum::fromType<ELFHelper::sym_info_type_e>();
        auto t0 = m.valueToKey(in & 0xF);
        p->setText(1, t0);
        QTWI(shh, "uchar sym_info_bind : 4", oo++, 1);
        m = QMetaEnum::fromType<ELFHelper::sym_info_bind_e>();
        auto t1 = m.valueToKey((in & 0xF0) >> 4);
        p->setText(1, t1);
        shh->setText(1, QString("%1 | %2").arg(t0).arg(t1));
        str = reader.read(oo, 1);
        QTWI(sh, "unsigned char sym_other", oo++, 1);
        p->setText(1, QString("%1").arg(TOCHARINT(str)));
        QTWI(sh, "Elf64_Half sym_shndx", oo, 2);
        p->setText(1, QString("%1").arg(TOUSHORT(reader.read(oo, 2))));
        oo += 2;
        QTWI(sh, "Elf64_Addr sym_value", oo, 8);
        p->setText(1, QString("0x%1").arg(TOINT64(reader.read(oo, 8)), 0, 16));
        oo += 8;
        QTWI(sh, "Elf64_Xword sym_size", oo, 8);
        p->setText(1, QString("%1").arg(TOINT(reader.read(oo, 8))));
        oo += 8;
      }
    }
    goto inf;
  }
err:
  return false;
inf:
  if (addmeta)
    controller.setMetaVisible(true);
  requestRelease();
  return true;
}

HookIndex WingElfPlugin::getHookSubscribe() { return HookIndex::None; }

void WingElfPlugin::unload() {}

uint WingElfPlugin::pluginVersion() { return 2; }

QMenu *WingElfPlugin::registerMenu() { return menu; }

void WingElfPlugin::registerDockWidget(
    QHash<QDockWidget *, Qt::DockWidgetArea> &rdw) {
  rdw.insert(dw, Qt::DockWidgetArea::BottomDockWidgetArea);
}

QToolButton *WingElfPlugin::registerToolButton() { return tbtn; }
